package ui.shell;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;

import network.ListenerBindException;
import network.SendSignalException;
import network.config.UnsupportedProtocol;

import controller.MessageController;
import controller.UserAlreadyConnected;
import controller.UserNotConnected;
import file.BadFileTypeException;
import file.TransferFile;
import file.TransferFileObserver;
import file.manager.FileManagerStartException;
import ui.MessageObserver;
import user.User;
import user.UsersListObserver;

public class ShellInterface implements MessageObserver, UsersListObserver, TransferFileObserver {
	
	private AsyncStreamReader scan ;
	private ArrayList <User> users ;
	private MessageController msgCtrl ;
	
	private void startMessage () {
		System.out.println("--- --- --- --- ---") ;
		System.out.println("--- Chat System ---") ;
		System.out.println("--- --- --- --- ---") ;
		System.out.println() ;
	}
	
	public ShellInterface () {
		super () ;
		startMessage () ;
		this.scan = new AsyncStreamReader(System.in) ;
		this.users = new ArrayList <User> () ;
		this.msgCtrl = new MessageController() ;
		this.msgCtrl.addMessageObserver(this) ;
		this.msgCtrl.addTransferFileObserver(this) ;
		this.msgCtrl.addUsersListObserver(this) ;
	}
	
	public void launch () {
		boolean keep = true ;
		while (keep) {
			displayMenu () ;
			System.out.print("Votre choix : ") ;
			int choice ;
			try {
				choice = this.scan.readInt() ; ;
			}
			catch (Exception e) {
				choice = -1 ;
				this.scan.readLine() ;
			}
			switch (choice) {
			case 1:
				connect () ;
				break ;
			case 2:
				showUsers () ;
				break ;
			case 3:
				sendMessage () ;
				break ;
			case 4:
				sendFile() ;
				break ;
			case 5:
				disconnect () ;
				keep = false ;
				break ;
			default:
				System.out.println("Bad choice !") ;
			}
		}
	}

	private void disconnect() {
		if (!this.msgCtrl.connected()) {
			System.out.println("Vous devez être connecté !") ;
		}
		else {
			try {
				this.msgCtrl.performDisconnect() ;
			} catch (SendSignalException e) {
				e.printStackTrace();
			} catch (UserNotConnected e) {
				e.printStackTrace();
			}
			System.out.println("Au revoir !") ;
		}		
	}
	
	private void sendFile () {
		if (!this.msgCtrl.connected()) {
			System.out.println("Vous devez être connecté !") ;
		}
		else {
			System.out.print("Numéro de l'utilisateur qui recevra le fichier : ") ;
			int userTo = this.scan.readInt() ;
			System.out.print("Fichier : ") ;
			String filename = this.scan.readNext () ;
			try {
				this.msgCtrl.sendFile(new File(filename), this.users.get(userTo)) ;
				System.out.println("Demande envoyé à " + this.users.get(userTo).getUsername() + "!") ;
			} 
			catch (SendSignalException e) {
				e.printStackTrace();
			} 
			catch (BadFileTypeException e) {
				System.out.println("Fichier incorrect !") ;
			}
			catch (IndexOutOfBoundsException e) {
				System.out.println("No user found at " + String.valueOf(userTo)) ;
			}
		}			
	}

	private void sendMessage() {
		if (!this.msgCtrl.connected()) {
			System.out.println("Vous devez être connecté !") ;
		}
		else {
			System.out.print("Numéro de l'utilisateur qui recevra le message : ") ;
			int userTo = this.scan.readInt() ;
			System.out.print("Message : ") ;
			String msg = this.scan.readNext () ;
			try {
				HashSet <User> to = new HashSet <User> () ;
				to.add(this.users.get(userTo)) ;
				this.msgCtrl.sendText(to, msg);
				System.out.println("Message envoyé à " + this.users.get(userTo).getUsername() + "!") ;
			} 
			catch (SendSignalException e) {
				e.printStackTrace();
			} 
			catch (UserNotConnected e) {
				e.printStackTrace();
			}
			catch (IndexOutOfBoundsException e) {
				System.out.println("No user found at " + String.valueOf(userTo)) ;
			}
		}			
	}

	private void showUsers() {
		System.out.println("Utilisateurs en ligne : ") ;
		for (int i=0; i<this.users.size(); i++) {
			System.out.println(" " + String.valueOf(i) + " - " + this.users.get(i)) ;
		}
	}

	private void connect() {
		if (this.msgCtrl.connected()) {
			System.out.println("Vous êtes déjà connecté !") ;
		}
		else {
			System.out.print("Login : ") ;
			try {
				this.msgCtrl.performConnect(this.scan.readNext()) ;
			} catch (SendSignalException e) {
				System.err.println("Unable to connect : Hello send failed.") ;
			} catch (ListenerBindException e) {
				System.err.println("Unable to bind the UDP listener.") ;
			} catch (UserAlreadyConnected e) {

			} catch (FileManagerStartException e) {
				System.err.println("Unable to start the file manager.") ;
			} catch (UnsupportedProtocol e) {
				e.printStackTrace();
			}
		}
	}

	private void displayMenu() {
		System.out.println("Menu : ") ;
		System.out.println(" 1 - Connect") ;
		System.out.println(" 2 - Show Users") ;
		System.out.println(" 3 - Send Message") ;
		System.out.println(" 4 - Send file") ;
		System.out.println(" 5 - Disconnect") ;
	}

	@Override
	public void fireNewMessage (User from, ArrayList <User> to, String text) {
		System.out.println(from.getIdentifier() + " : " + text) ;		
	}
	

	@Override
	public void fireLocalFileQuery (TransferFile tf) {
	
	}

	@Override
	public void fireFileResponse (TransferFile tf,
			boolean accepted) {
		if (accepted) {
			System.out.println(tf.getReceiver().getIdentifier() + " has accepted " + tf.getFileName()) ;
		}
		else {
			System.out.println(tf.getReceiver().getIdentifier() + " has refused " + tf.getFileName()) ;
		}
	}

	@Override
	public void startFileTransfer(TransferFile tf) {
		if (tf.send()) {
			System.out.println("Start sending of " + tf.getFileName()) ;
		}
		else {
			System.out.println("Start reception of " + tf.getFileName()) ;
		}
	}

	@Override
	public void updateFileTransfer(TransferFile tf, int transferValue) {
		
	}

	@Override
	public void stopFileTransfer(TransferFile tf, boolean correct) {
		if (correct) {
			System.out.println("Transfer of " + tf.getFileName() + " has succeed.") ;
		}
		else {
			System.out.println("Transfer of " + tf.getFileName() + " has failed.") ;
		}
	}

	@Override
	public void fireRemoteFileQuery (TransferFile tf) {
		System.out.print("Do you want to save : " + tf.getFileName() + "(" + String.valueOf(tf.getSize()) + " byte(s)), (y/n) : ") ;
		String rep = this.scan.readNext() ;
		if (rep.equals("n")) {
			try {
				this.msgCtrl.sendFileRefused(tf) ;
			} 
			catch (SendSignalException e) {
			}
		}
		else {
			System.out.print("Save the file to : ") ;
			try {
				this.msgCtrl.sendFileAccepted(new TransferFile(tf, this.scan.readNext())) ;
			} 
			catch (SendSignalException e) {
			}
		}
	}

	@Override
	public void fireNewUser(User u) {
		System.out.println("Nouvelle utilisateur : " + u.getIdentifier()) ;
		this.users.add(u) ;
	}

	@Override
	public void fireUpdateUser(User oldUser, User newUser) {
		System.out.println(oldUser.getIdentifier() + " est maintenant " + newUser.getIdentifier()) ;
		this.users.set(this.users.indexOf(oldUser), newUser) ;
	}

	@Override
	public void fireDeleteUser(User u) {
		System.out.println(u.getIdentifier() + " a quitté le système.") ;
		this.users.remove(u) ;
	}

	public static void main (String args[]) throws IOException {
		(new ShellInterface()).launch() ;
	}
	
}
